/**
 *
#============== 中文 =================
## 简介
    本 SDK 仅封装了传输协议和网络调用，编解码部分需要调用者自行实现。基于 C++ 11 开发。
## API 流程

* 检索投屏

    初始化 -> 检索局域网设备 -> 连接接收端 -> 投屏 -> 结束投屏

* 投屏码投屏

    初始化 -> 使用投屏码连接接收端 -> 投屏 -> 结束投屏

## API 说明
  请参考每个API前面的代码说明。

## 注意事项
    API 返回的 USBDisplayClientSDKErrorCode 仅代表调用这个API的参数有没有问题，并不能保证一些耗时的操作的结果。一些耗时的操作都是通过回调进行通知的。

 *#============== English ============

 ## README
    This SDK only encapsulate protocol and network, and it should app's job to capture screen, encode it to H264 data, or decode H264 data and display it on View.
    It is developed using C++ 11

## API flow
 * Search and Share Screen
   init -> search receiver on local network -> connect receiver -> share screen -> stop share screen

 * Share screen using Pin Code
   init -> input pin code and connect receiver with pin code -> share screen -> stop share screen

## API specification
   please refer the comment on every API

## Note
   Every API method has a return value: USBDisplayClientSDKErrorCode, this value only mean the calling parameter is valid or not, the calling result would be
   feed back from callbacks.


 *
 *
 *
 */



#ifndef USBDISPLAYCLIENTSDK_USBDISPLAYCLIENTSDK_H
#define USBDISPLAYCLIENTSDK_USBDISPLAYCLIENTSDK_H

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif  /* __cplusplus */

#define USBDISPLAY_CLIENT_SDK_VERSION                 "1.0.10"

#if defined (_WIN32)
#define USBDISPLAY_CLIENT_SDK_API __declspec(dllexport)
#else
# define USBDISPLAY_CLIENT_SDK_API __attribute__ ((visibility ("default")))
#endif

/**
 * (CN)错误码枚举值。
 * (EN)Error code. The return value of calling an API, or error code return from receiver.
 */
typedef enum _USBDisplayClientSDKErrorCode {
    USBDISPLAY_CLIENT_ERROR_CODE_OK = 0x00, // 没有错误。 NO Error.
    USBDISPLAY_CLIENT_ERROR_INVALID_PARAMETER = 0X01, // 请求的参数有不合法数据. Some request parameters is invalid
    USBDISPLAY_CLIENT_ERROR_PERMISSION_DENIAL = 0X02, // 服务器不允许操作，比如接收端在独占模式。
    USBDISPLAY_CLIENT_ERROR_DISPLAY_NOT_CONNECTED = 0X03, // 没有与接收端连接就进行其它操作。The client is not connected to receiver, please connect it first.
    USBDISPLAY_CLIENT_ERROR_PIN_CODE_SERVER_ADDRESS_INVALID = 0X04, // 6位投屏码的服务器地址不合法。 The configure pin code server address is invalid.
    USBDISPLAY_CLIENT_ERROR_PIN_CODE_NO_EXIST = 0X05, // 通过6位投屏码找不到接收端. The input pin code is no exist on server.
    USBDISPLAY_CLIENT_ERROR_AUTHENTICATE_PASSED = 0X06, // 投屏密码的鉴权通过。 The input pin code or password pass the authentication.
    USBDISPLAY_CLIENT_ERROR_AUTHENTICATE_FAILED = 0X07, // 投屏密码的鉴权失败. Authenticate failed.
    USBDISPLAY_CLIENT_ERROR_AUTHORIZE_WAITING   = 0x08, // 投屏需要授权，现在等待中。 Authorize waiting.
    USBDISPLAY_CLIENT_ERROR_BUFFER_FULL_OR_INVALID = 0x09, // 发送缓冲区满了，或者当前不可用。
    USBDISPLAY_CLIENT_ERROR_NOT_AUTHORIZE_ACCESS = 0X0A, //接收端有密码保护，需要请求密码校验。
    USBDISPLAY_CLIENT_UNKNOWN_ERROR = 0xff, // 其它未知异常. Other unknown exception.
} USBDisplayClientSDKErrorCode;

/**
 * (CN)触控事件的枚举值.
 * (EN)touch action.
 *
 * click: touch down, touch up.
 * drag: touch down, touch move, touch up.
 * double click: touch down, Touch Up, Touch Down, Touch Up
 */
typedef enum _UsbDisplayTouchAction {
    USBDISPLAY_TOUCH_DOWN = 0x00, // 按下. Touch Down action.
    USBDISPLAY_TOUCH_MOVE = 0x01, // 移动. Touch Move action.
    USBDISPLAY_TOUCH_UP = 0x02 // 抬起. Touch Up action.
} UsbDisplayTouchAction;

/**
 * 触控事件结构体
 */
typedef struct _UsbDisplayTouchEvent {
    UsbDisplayTouchAction action; // 触控动作. Touch Action: Down, Move, Up
    /**
     * (EN)Touch id. every touch point has a unique id. When touch down, grant a not used ID which is great or equal 0, when touch up, recycle this touch id.
     * (CN)触控的 id， 在按下时确定，在移动过程中不应该变化。一般情况下，它的值从 0 开始，第一个点的 id 为0， 第二个点的 id 为 1，当抬起后，这个 id 值又可以再次使用。
     *
     */
    int id; //
    int rawX; // 触控点的坐标值 X。 Touch x position of touch point
    int rawY; // 触控点的坐标值 Y。 Touch y position of touch point
} UsbDisplayTouchEvent;

/**
 * (CN)客户端类型。需要把客户端的类型告诉接收端，这样接收端就可以根据客户端类型进行一些特殊处理。
 * (EN)Client type constants. The receiver need to know the client type, and do some special operation on it.
 */
typedef enum _USBDisplayClientType {
    USBDISPLAY_CLIENT_TYPE_UNKNOWN = 0X00,
    USBDISPLAY_CLIENT_TYPE_WINDOWS_DONGLE = 0X01,
    USBDISPLAY_CLIENT_TYPE_MAC_DONGLE = 0X02,
    USBDISPLAY_CLIENT_TYPE_WINDOWS_WIFI = 0X03,
    USBDISPLAY_CLIENT_TYPE_ANDROID = 0X04,
    USBDISPLAY_CLIENT_TYPE_MAC_WIFI = 0X05,
    USBDISPLAY_CLIENT_TYPE_IOS_AIRPLAY = 0X06,
    USBDISPLAY_CLIENT_TYPE_IOS_CLIENT = 0X07,
    USBDISPLAY_CLIENT_TYPE_IOS_MIX_CLIENT = 0X08,
    USBDISPLAY_CLIENT_TYPE_MIRROR_BACK = 0X09,
    USBDISPLAY_CLIENT_TYPE_LINUX_WIFI = 0X0B,
    USBDISPLAY_CLIENT_TYPE_LINUX_DONGLE = 0X0C,
} USBDisplayClientType;

typedef enum _USBDisplayClientAudioFormat {
    USBDISPLAY_AUDIO_FORMAT_PCM = 0X01,
    USBDISPLAY_AUDIO_FORMAT_MP3 = 0X02,
    USBDISPLAY_AUDIO_FORMAT_AAC = 0X03,
    USBDISPLAY_AUDIO_FORMAT_MP2 = 0X04,
} USBDisplayClientAudioFormat;

typedef enum _USBDisplayClientAudioSampleRate {
    USBDISPLAY_AUDIO_SAMPLE_RATE_44100 = 0X01,
    USBDISPLAY_AUDIO_SAMPLE_RATE_48000 = 0X02,
    USBDISPLAY_AUDIO_SAMPLE_RATE_32000 = 0X03,
    USBDISPLAY_AUDIO_SAMPLE_RATE_24000 = 0X04,
    USBDISPLAY_AUDIO_SAMPLE_RATE_20500 = 0X05,
    USBDISPLAY_AUDIO_SAMPLE_RATE_16000 = 0X06,
    USBDISPLAY_AUDIO_SAMPLE_RATE_8000  = 0X07
} USBDisplayClientAudioSampleRate;

/**
 * 服务端支持的功能结构体
 */
typedef struct _UsbDisplaySupportFunctions {
    bool mirrorBack;//反镜像. Mirror Back: Client can get the screen of the receiver.
    bool snapShot; // 一截屏. snapshot the receiver's screen.
    bool mouseAccelerate; // 鼠标加速. Mouse Accelerate which make the mouse more smoothly.
    bool bluetooth; // 蓝牙. Bluetooth which can enable Android client to share audio via bluetooth
    bool annotation; // 同步批注. Multiple client device do annotation at the same time.
    bool audioTypePcm; // 支持PCM的音频格式. Receiver can play raw pcm data.
    bool audioTypeAac; // 支持AAC的音频格式. Receiver can play aac data
    bool androidAudio; // 支持Android客户端投屏传声音。Receiver support Android client to send audio via network.
    bool supportVideoFormatH265;//接收端是否支持H265的视频格式
    bool supportVideoVP8;
    bool supportVideoVP9;
    bool byom; // 接收端是否支持 BYOM 功能
} UsbDisplaySupportFunctions;

/**
 * (CN)接收端的授权类型
 * (EN)Receiver authenticate type
 */
typedef enum _UsbDisplayAuthenticateType {
    USBDISPLAY_AUTHENTICATE_TYPE_NONE = 0x00, // 不需要密码. No password needed
    USBDISPLAY_AUTHENTICATE_TYPE_PASSWORD = 0X01, // 有密码保护. The receiver is protected by password, and the client need to do authenticate before further operation.
} UsbDisplayAuthenticateType;

/**
 * 分辨率的枚举类型
 */
typedef enum _UsbDisplayClientResolution {
    USBDISPLAY_RESOLUTION_240P = 0x01,
    USBDISPLAY_RESOLUTION_360P = 0x02,
    USBDISPLAY_RESOLUTION_480P = 0x03,
    USBDISPLAY_RESOLUTION_720P = 0x04,
    USBDISPLAY_RESOLUTION_1080P = 0x05,
} UsbDisplayClientResolution;

typedef enum _UsbDisplayCLientLogLevel {
    USBDISPLAY_CLIENT_LOG_VERBOSE = 0X01,
    USBDISPLAY_CLIENT_LOG_INFO = 0X02,
    USBDISPLAY_CLIENT_LOG_WARN = 0X03,
    USBDISPLAY_CLIENT_LOG_ERROR = 0X04,
    USBDISPLAY_CLIENT_LOG_FATAL = 0X05,
} UsbDisplayClientLogLevel;

/**
 * (CN)要求客户端上传的画面是否镜像模式
 * 所谓镜像，就是画面左右对调。如果需要上下对调，就需要开启镜像的同时用前面的旋转180度。
 * (EN)mirror the screen video data.
 */
typedef enum _UsbDisplayScreenMirror {
    USBDISPLAY_SCREEN_MIRROR_DISABLED = 0,
    USBDISPLAY_SCREEN_MIRROR_ENABLED = 1,
} UsbDisplayScreenMirror;

/**
* (CN)要求客户端投屏过来的画面旋转角度
 * (EN)Rotate the screen data.
*/
typedef enum _UsbDisplayRotationDegree {
    USBDISPLAY_ROTATION_DEGREE_0 = 0,
    USBDISPLAY_ROTATION_DEGREE_90 = 1,
    USBDISPLAY_ROTATION_DEGREE_180 = 2,
    USBDISPLAY_ROTATION_DEGREE_270 = 3,
} UsbDisplayRotationDegree;

typedef struct _PinCodeReceiverInfo {
    char name[40]; // receive name
    char version[20];// receiver version
    char ipList[5][20];// ip list. so the client should try every ip to connect the receiver
    int ipSize; // ip list size.
} PinCodeReceiverInfo;

typedef enum _UsbDisplayAuthorizeResult {
    USBDISPLAY_AUTHORIZE_PASS = 0X01,
    USBDISPLAY_AUTHORIZE_REJECTED = 0X02,
}UsbDisplayAuthorizeResult;

typedef struct _USBDisplayClientCallback {
    /**
     * (CN)当开始调用 UsbDisplayClientConnect 时，SDK 在尝试连接服务器时回调
     * (EN)when request UsbDisplayClientConnect, would callback this before SDK try to connect receiver
     *
     *
     * @param cls
     * @param ip (CN)接收端的 IP 地址. (EN)The Receiver's ip address
     */
    void (*onStartConnectingToReceiver)(const void *cls, const char *ip);

    /**
     * (CN)SDK 成功连接到接收端
     * (EN)SDK connect receiver successfully.
     *
     * @param cls
     * @param ip 接收端的 IP 地址
     */
    void (*onConnectedReceiverSuccess)(const void *cls, const char *ip);

    /**
     * (CN)SDK 断开与服务器的连接。可能是主动断开，也可能是 UsbDisplayClientConnect 时无法连接服务器。超时断开会也回调
     * (EN)SDK disconnect from receiver. Maybe disconnected by client itself, or failed to connect to receiver. and may also got timeout.
     *
     * @param cls
     * @param ip 接收端的 IP 地址
     */
    void (*onDisconnectFromReceiver)(const void *cls, const char *ip);

    /**
     * (CN)当SDK 成功连接到服务端之后，已完成了跟服务器基本信息的交换。此时就可以尝试调用
     * (EN)When SDK successfully connected to receiver, it would exchange data with receiver. When it finished, this READY callback invoked.
     * And then you can call these functions:
     * UsbDisplayClientGetSupportFunctions and UsbDisplayClientGetReceiverAuthenticateType
     *
     * @param cls
     * @param ip 接收端的 IP 地址
     */
    void (*onConnectToReceiverReady)(const void *cls, const char *ip);

    /**
     * (CN)请求投屏成功，此时可以给接收端发送视频和音频数据
     * (EN)Request share screen success, now the client can capture screen, encode it, and then send to receiver.
     *
     * @param cls
     * @param ip 接收端的 IP 地址
     */
    void (*onRequestStartShareScreenSuccess)(const void *cls, const char *ip, int frameRate, UsbDisplayClientResolution resolution, int gop, int bitrate);

    /**
     * (CN)请求投屏失败。具体原因请查看 code
     * (EN)Request share screen failed. please refer the <B> code </B> for detail.
     *
     * @param cls
     * @param ip
     * @param code
     */
    void (*onRequestStartShareScreenFailed)(const void *cls, const char *ip, USBDisplayClientSDKErrorCode code);

    /**
     * (CN)请求结束投屏成功，一般都会成功
     * (EN)Request stop share screen success.
     *
     * @param cls
     * @param ip
     */
    void (*onRequestStopShareScreenSuccess)(const void *cls, const char *ip);

    /**
     * (CN)请求结束投屏失败。具体原因请看 code
     * (EN)Request stop share screen failed. pleas refer to <B>code</B> for detail.
     *
     * @param cls
     * @param ip
     * @param code
     */
    void (*onRequestStopShareScreenFailed)(const void *cls, const char *ip, USBDisplayClientSDKErrorCode code);

    /**
     * (CN)请求反镜像成功。 此时接收端就会返回反镜像的视频数据
     * (EN)Request mirror back success, then would receive mirror video data from receiver
     *
     * @param cls
     * @param ip
     */
    void (*onRequestStartMirrorBackSuccess)(const void *cls, const char *ip);

    /**
     * (CN)请求反镜像失败。具体原因请查看 code
     * (EN)Request mirror back failed. please refer the error code for detail.
     *
     * @param cls
     * @param ip
     * @param code
     */
    void (*onRequestStartMirrorBackFailed)(const void *cls, const char *ip, USBDisplayClientSDKErrorCode code);

    /**
     * 请求结束反镜像成功
     * Request stop mirror back success.
     * @param cls
     * @param ip
     */
    void (*onRequestStopMirrorBackSuccess)(const void *cls, const char *ip);

    /**
     * (CN)请求结束反镜像失败。具体原因请查看 code
     * (EN)Request stop mirror back failed. please refer the error code for detail
     *
     * @param cls
     * @param ip
     * @param code
     */
    void (*onRequestStopMirrorBackFailed)(const void *cls, const char *ip, USBDisplayClientSDKErrorCode code);

    /**
     * (CN)接收端主动请求客户端上屏。此时客户端需要完成一次请求上屏的操作，当成功后传输视频和音频数据
     * (EN)The receiver request client to share screen. If the client accept the request, then client should call request share screen.
     *
     * @param cls
     * @param ipm
     */
    void (*onServerRequestStartShareScreen)(const void *cls, const char *ip);

    /**
     * (CN)接收端请求结束投屏。此时客户端需要请求结束投屏，关闭视频和音频数据
     * (EN)Receiver request client to stop share screen. The client should request stop share screen, and then close the video and audio data.
     *
     * @param cls
     * @param ip
     */
    void (*onServerRequestStopShareScreen)(const void *cls, const char *ip);

    /**
     * (CN)接收端请求客户端开始反镜像。
     * (EN)Receiver request client to start mirror back. Client should open a display view and decode the h264 data.
     *
     * @param cls
     * @param ip
     */
    void (*onSeverRequestStartMirrorBack)(const void *cls, const char *ip);

    /**
     * (CN)接收端请求客户端结束反镜像
     * (EN)Receiver request client to stop mirror back. Client should stop the decoding thread and shutdown the display view.
     *
     * @param cls
     * @param ip
     */
    void (*onServerRequestStopMirrorBack)(const void *cls, const char *ip);

    /**
     * (CN)接收端发回触摸回传数据
     * (EN)Receiver touch back to client.
     *
     * @param cls
     * @param ip 接收端的 IP. Receiver ip address/
     * @param count 触摸点个数. Touch point count
     * @param events 触摸点的数据. Touch data.
     */
    void (*onReceiverTouchBack)(const void *cls, const char *ip, int count, UsbDisplayTouchEvent *events);

    /**
     * (CN)接收端发回反镜像视频数据，h264 格式。其中 I 帧前面会有 sps 和 pps。
     * (EN)Receiver send mirror back video data(h264).
     *
     * @param cls
     * @param ip 接收端的IP信息
     * @param buf 视频 h264 数据
     * @param length 数据长度
     * @param timestamp 时间戳
     */
    void (*onReceiverMirrorBackVideoFrame)(const void *cls, const char *ip, const uint8_t *buf, int length, int timestamp);

    /**
     * 接收端发回视频格式数据。在默认情况下，发回的数据是 h264, 当不是这个格式时或者在 BYOM 模式下，就会回调这个接口，先返回视频格式数据，然后再发视频数据
     * @param cls
     * @param ip
     * @param format 视频格式。1： YUYV； 2：MJPG， 3：H264
     * @param resolution 视频分辨率。1： 480； 2：720； 3：1080
     * @param fps 视频的帧率。只是一个目标值，实际传输会在这个附近浮动
     */
    void (*onReceiverMirrorBackVideoInfo)(const void *cls,
            const char *ip,
            int format,
            int resolution,
            int fps);

    /**
     * (CN)接收端发回返镜像的音频数据。
     *
     * @param cls
     * @param ip
     * @param buf
     * @param length
     * @param timestamp
     */
    void (*onReceivedMirrorBackAudioFrame)(const void *cls, const char *ip, const uint8_t *buf, int length, int timestamp);

    /**
     * 接收端发回反镜像的音频格式信息。
     * @param cls
     * @param ip 接收端的 IP
     * @param format 格式. 1: PCM, 2: AAC, 3: MP3
     * @param sampleRate 采样率。 1： 44100， 2：48000
     * @param channelCount 声道数。
     */
    void (*onReceivedMirrorBackAudioInfo)(const void* cls,
            const char* ip,
            int format,
            int sampleRate,
            int channelCount);

    /**
     * (CN)跟接收端失联了。
     * (EN)Lost connection with receiver
     *
     * @param cls
     * @param ip
     */
    void (*onLostReceiverConnection)(const void *cls, const char *ip);

    /**
     * (CN)接收端请求更新帧率和分辨率。接收端会根据需要要求客户端调整分辨率和码率信息
     * (EN)Receiver request update frame rate and resolution. The receiver would request client to update resolution, frame rate etc. base on the client count and network latency.
     *
     * @param cls
     * @param ip 服务器地址
     * @param framerate 帧率,具体的帧率值. frame rate.
     * @param resolution (CN)分辨率. 支持240P 到 1080P. (EN)Resolution, please refer UsbDisplayClientResolution for more detail.
     * @keyFrameInterval (CN)关键帧间隔。它的值与帧率之积就是设置给h264的关键帧间隔时间。比如它的值是5，前面framerate的帧率是10，那么设置给h264的关键帧间隔时间就是5 x 10 = 50.
     * (EN)Key frame interval. Its value is the interval between two key frame in seconds.
     * @bitRate (CN)码率 它的值的单位是 10KB/s。比如它的值是6，代表码率是60KB/s。注：这里是大B。
     * (EN)The bitrate, its unit is 10 KB/s. If its value is 6, then the actual value is 6 * 10KB/s = 60KB/s
     * @degree (CN)旋转角度。支持 0， 90， 180， 270 度. (EN)Rotation client screen degree.
     * @mirror (CN)镜像。就是左右对换。如果需要上下对换，那么就旋转180度，再加个镜像。 (EN)Mirror the client screen.
     */
    void (*onServerRequestUpdateFrameRate)(const void *cls,
                                           const char *ip,
                                           int framerate,
                                           UsbDisplayClientResolution resolution,
                                           int keyFrameInterval,
                                           int bitRate,
                                           UsbDisplayRotationDegree degree,
                                           UsbDisplayScreenMirror mirror);

    /**
     * (CN)在 UsbDisplayClientStartQueryServers 检索到局域网下有接收端数据
     * (EN)Found receiver on local network by calling <I>UsbDisplayClientStartQueryServers</I>
     *
     * @param cls
     * @param ip 接收端的IP. Receiver's ip address
     * @param version 接收端的版本. Receiver's version.
     * @param name 接收端的名字. Receiver's name
     * @param localAddress (CN)本地网络的地址。这个地址在进行 UsbDisplayClientConnect 需要用上. (EN)Local device ip address, need to use this address when Connect receiver.
     */
    void (*onFoundOneReceiver)(const void *cls,
                               const char *ip,
                               const char *version,
                               const char *name,
                               const char *localAddress);

    /**
     * (CN)通过 6 位数字投屏返回检索的结果。
     * (EN)Got response from Pin Code server.
     *
     * @param cls
     * @param code 如果是 USBDISPLAY_CLIENT_ERROR_CODE_OK 代表成功，其它代表失败. Only if the code == USBDISPLAY_CLIENT_ERROR_CODE_OK means found, others means failed.
     * @param receiverInfo 只在 code == USBDISPLAY_CLIENT_ERROR_CODE_OK，这里才会有信息。Only has data when response ok.
     */
    void (*onPinCodeServerResponseReceiverAddress)(const void *cls,
                                                   USBDisplayClientSDKErrorCode code,
                                                   const PinCodeReceiverInfo* receiverInfo);

    /**
     * (CN)接收端返回投屏密码的鉴权结果。
     * (EN)Got authenticate device password result.
     *
     * @param cls
     * @param code USBDISPLAY_CLIENT_ERROR_AUTHENTICATE_PASSED or USBDISPLAY_CLIENT_ERROR_AUTHENTICATE_FAILED
     */
    void (*onServerResponseAuthenticatePasswordResult)(const void *cls, USBDisplayClientSDKErrorCode code);

    /**
     * (CN)接收端返回投屏码的鉴权结果。因为接收端有投屏密码，所以客户端需要发送投屏码到接收端进行鉴权。
     * (EN)Got authenticate pin code result
     *
     * @param cls
     * @param code USBDISPLAY_CLIENT_ERROR_AUTHENTICATE_PASSED or USBDISPLAY_CLIENT_ERROR_AUTHENTICATE_FAILED
     */
    void (*onServerResponseAuthenticatePinCodeResult)(const void* cls, USBDisplayClientSDKErrorCode code);

    /**
     * (CN)通过8位投屏找到了接收端
     * (EN)Found receiver using 8 digits pin code
     *
     *
     * @param cls
     * @param ip 接收端的 IP
     * @param name 接收端的名字
     * @param version 接收端的版本
     * @param localAddress 本地的地址，这个在连接接收端时需要用到。因为本地设备可能会有多个网卡，所以这个本地信息代表了与接收端对应的网卡的 IP 信息。
     */
    void (*onQueryReceiverBy8PinCodeFoundReceiver)(const void *cls,
                                                   const char *ip,
                                                   const char *name,
                                                   const char *version,
                                                   const char *localAddress);

    /**
     * (CN)通过 8 位投屏码未能找到接收端
     * (EN)Found none using 8 digits pin code
     *
     * @param cls
     */
    void (*onQueryReceiverBy8PinCodeFoundNone)(const void *cls);

    void (*onQueryReceiverBy5LocalPinCodeFoundReceiver)(
            const void *cls,
            const char *ip,
            const char *name,
            const char *version,
            const char *localAddress);

    void (*onQueryReceiverBy5LocalPinCodeFoundNone)(const void *cls);

    void (*onQueryReceiverBy6LocalPinCodeFoundReceiver)(
            const void *cls,
            const char *ip,
            const char *name,
            const char *version,
            const char *localAddress);

    void (*onQueryReceiverBy6LocalPinCodeFoundNone)(const void *cls);

    /**
     * (CN)通过 IP 找到了接收端
     * (EN)Found the receiver using IP
     *
     * @param cls
     * @param ip 接收端的 IP
     * @param name 接收端的名字
     * @param version 接收端的版本
     * @param localAddress 本地网卡的 IP
     */
    void (*onQueryReceiverInfoByIpFoundReceiver)(const void *cls,
                                                 const char *ip,
                                                 const char *name,
                                                 const char *version,
                                                 const char *localAddress);

    /**
     * (CN)通过 IP 未能找到接收端
     * (EN)Found one using IP
     *
     *
     * @param cls
     * @param ip
     */
    void (*onQueryReceiverInfoByIpFoundNone)(const void *cls, const char* ip);


    /**
     * (CN)日志回调。SDK 会回调一些日志回来，用于调试分析问题。
     * (EN)Log callback. SDK would callback some log base on the log level setting.
     *
     * @param level 日志级别。
     * @param msg 消息内容
     */
    void (*onLogMsg)(UsbDisplayClientLogLevel level, const char* msg);

    void (*onAuthorizeResult)(const void *cls, UsbDisplayAuthorizeResult result);
} USBDisplayClientCallback;

typedef struct _USBDisplayClientParameter {
    void *cls; // 一个附加数据，由客户端传入，可以用于区分不同实例的 SDK。然后在每个回调都会带回这个数据。

    char deviceName[40]; // 设备名字
    char deviceId[12];// 设备ID，主要用于接收端区分唯一性。可以使用去掉冒号的Mac地址作为设备ID

    USBDisplayClientType clientType; // 客户端类型

    USBDisplayClientCallback callback; // 回调列表
    UsbDisplayClientLogLevel logLevel; // 日志级别，低于这个级别的日志不会返回。
    int sdkType;
} USBDisplayClientParameter;


typedef struct USBDisplayClientSDK UsbDisplayClientContext;

/**
 * (CN)初始化SDK。把相关参数传入SDK进行初始化。在调用其它 API 之前，需要先调用这个接口对 SDK 进行数据化，然后才可以可以调用其它接口。
 * (EN)Initialize SDK. Pass in some parameter to initial the SDK. Need to call this initial SDK function before calling others.
 *
 * @param context 传入一个空的引用，SDK 会对它进行初始化，然后就可以使用它调用其它接口。
 * @param parameter 相关的参数。用于初始化SDK和基础信息。
 * @return
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode UsbDisplayClientInit(UsbDisplayClientContext **context,
                                                                            const USBDisplayClientParameter &parameter);


/**
 * (CN)连接服务器。当客户端与接收端在同一个局域内之后，就可以连接接收端了。连接的结果通过回调进行通知。
 * (EN)Connect the receiver. Make sure the client and the receiver are in the same network, and the connect result would be notify from callback.
 *
 * 回调：
 *  onStartConnectingToReceiver 开始连接接收端
 *  onConnectedReceiverSuccess 连接接收端成功，但是此时还不能操作，需要等 ready 回调
 *  onDisconnectFromReceiver 连接接收端失败
 *  onConnectToReceiverReady 与接收端交互完信息，可以进行操作了。
 *
 * @param context 通过 UsbDisplayClientInit 初始化的 context
 * @param localIp local device ip address, like 192.168.1.123
 * @param serverIp receiver ip address, like 192.168.1.213
 * @return
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode UsbDisplayClientConnect(
        UsbDisplayClientContext *context,
        const char *localIp,
        const char *serverIp);

/**
 * 断开与接收端的连接。
 * @param context
 * @return
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode UsbDisplayClientDisconnect(UsbDisplayClientContext *context);

/**
 * (CN)请求更新设备名字。当在投屏过程中更新了设备名字，可以通过此接口更新到接收端
 * (EN)Request Update device name. Call this method when device is connected.
 *
 * @param context
 * @param name 设备名字，不能超过40个字符
 * @return
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode UsbDisplayClientUpdateDeviceName(UsbDisplayClientContext *context,
                                                                                        const char *name);

/**
 * (CN)请求更新设备宽高。当在投屏过程中更新了屏幕分辨率，需要调用此接口更新接收端的信息
 * (EN)When client device's resolution changed, request server to update it.
 *
 * @param context
 * @param width 设备的宽。如: 1920
 * @param height 设备的高。如: 1080
 * @return
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode UsbDisplayClientUpdateDisplaySize(UsbDisplayClientContext *context,
                                                                                         int width,
                                                                                         int height);

/**
 * (CN)请求投屏。当在 ready 之后，可以调用此接收进行投屏。调用的结果会在回调中通知，需要根据回调决定是否进行传输音视频数据。
 * (EN)Request share screen. The request result would be notified from callback.
 *
 * @param context
 * @return
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode UsbDisplayClientRequestStartShareScreen(UsbDisplayClientContext *context);

/**
 * (CN)请求结束投屏。在投屏状态下调用此接口进行结束投屏操作。如果未在投屏，刚此接口无作用。
 * (EN)Request stop sharing screen, and the result would be notified from callback.
 *
 * @param context
 * @return
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode UsbDisplayClientRequestStopShareScreen(UsbDisplayClientContext *context);

/**
 * (CN)请求反镜像。前提需要接收端支持反镜像功能。检查接收端是否支持反镜像功能，在 ready 之后，通过 UsbDisplayClientGetSupportFunctions 获取接收端支持的功能
 * 检查其中 mirrorBack 是否支持反镜像。此接口的调用结果会在回调中通知，然后在收到 onRequestStartMirrorBackSuccess 就表示请求反镜像成功，可以开窗口，进行解码显示。
 * (EN)Request Mirror Back. Before request mirror back, need to check whether the receiver support mirror back feature. call UsbDisplayClientGetSupportFunctions
 * after the receiver is READY. The request mirror back result would be notified from callback. When got the onRequestStartMirrorBackSuccess callback,
 * then show a view and decode the received h264 stream.
 *
 *
 * @param context
 * @return
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode UsbDisplayClientRequestStartMirrorBack(UsbDisplayClientContext *context);

/**
 * (CN)结束反镜像
 * (EN)Stop Mirror Back
 * @param context
 * @return
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode UsbDisplayClientRequestStopMirrorBack(UsbDisplayClientContext *context);

/**
 * (CN)检索局域网的接收端设备。当本机有多个网卡，也会检索相应网卡内的接收端。
 * (EN)Start search receiver on local network. If the client device has multiple network interface, it would search all of them.
 * @param context
 * @return
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode UsbDisplayClientStartQueryServers(UsbDisplayClientContext *context);

/**
 * (CN)结束检索。当不需要检索功能时，需要关闭它，不然会占用系统资源。
 * (EN)Stop searching.
 *
 * @param context
 * @return
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode UsbDisplayClientStopQueryServers(UsbDisplayClientContext *context);

/**
 * (CN)发送视频数据。当请求投屏成功后，需要通过这个接口进行发送画面数据。
 * (EN)Send video data. When request share screen success, then use this API to send picture's h264 data.
 *
 *
 * @param context
 * @param buf 包含 h264 数据的视频数据，这是一帧的数据。
 * @param length 数据长度
 * @param timestamp 视频的时间戳
 * @return 错误码。如果发送成功，返回：@USBDISPLAY_CLIENT_ERROR_CODE_OK， 其它值代表发送出现异常。
 * 当返回 @USBDISPLAY_CLIENT_ERROR_BUFFER_FULL_OR_INVALID 时，表示网络可能出现较差的情况，需要上层减慢发送数据的速度。
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode UsbDisplayClientSendShareScreenVideoFrame(UsbDisplayClientContext *context,
                                                                                                 const char *buf,
                                                                                                 int length,
                                                                                                 int timestamp);

/**
 * (CN)发送视频数据格式。当连接接收端成功之后，且在发送视频数据之前，如果视频格式不是 h264，就需要提前发送一个格式包。
 * 1： H264 (默认值)
 * 2： H265
 * 3： VP8
 * 4： VP9
 *
 * @param context
 * @param format 视频格式
 * @return
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode UsbDisplayClientSendShareScreenVideoInfo(UsbDisplayClientContext *context,
                                                                                                int format);


/**
 * (CN)发送音频格式信息。在一次会话过程中首次发送音频数据，或者在投屏过程中有音频格式变化，都需要通过此接口通知接收端。如果不调用此接口，接收端会默认数据为MP3， 44100， 双声道
 * (EN)Send audio info data. Tell the receiver what audio data is sending. When every time audio info changed, need to call this API. By default,
 * the audio format is MP3, sample rate is 44100, double audio channel. Call this API before <B>UsbDisplayClientSendShareScreenAudioFrame</B>
 *
 *
 * @param context
 * @param format 音频的格式：MP3, AAC, PCM 等
 * @param rate 采样率。44100, 48000等
 * @param channelCount 声道数。双声道，单声道
 * @return
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode UsbDisplayClientSendShareScreenAudioInfo(UsbDisplayClientContext *context,
                                                                                                USBDisplayClientAudioFormat format,
                                                                                                USBDisplayClientAudioSampleRate rate,
                                                                                                char channelCount);

/**
 * (CN)发送音频数据。从客户端发送的声音数据，需要在 UsbDisplayClientSendShareScreenAudioInfo 发送音频格式之后，才调用此接口，不然接收端会默认当前的数据是 mp3 格式的数据。
 * (EN)Send audio data. Should let receiver know the audio format before sending audio data. Or the receiver would treat it as mp3.
 *
 * @param context
 * @param buf 音频数据。
 * @param length 长度
 * @param timestamp 时间戳
 * @return 错误码。如果发送成功，返回：@USBDISPLAY_CLIENT_ERROR_CODE_OK， 其它值代表发送出现异常。
 * 当返回 @USBDISPLAY_CLIENT_ERROR_BUFFER_FULL_OR_INVALID 时，表示网络可能出现较差的情况，需要上层减慢发送数据的速度。
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode UsbDisplayClientSendShareScreenAudioFrame(UsbDisplayClientContext *context,
                                                                                                 const char *buf,
                                                                                                 int length,
                                                                                                 int timestamp);

/**
 * (CN)在连接接收端 ready 之后，通过此接收口获取接收端支持的功能列表。客户端根据这个接收返回的信息，在界面上显示一些操作入口。
 * (EN)When receiver is READY, call this API to get receiver's ability. And the client behave base on the result.
 *
 * @param context
 * @return
 */
USBDISPLAY_CLIENT_SDK_API const UsbDisplaySupportFunctions &UsbDisplayClientGetSupportFunctions(UsbDisplayClientContext *context);

/**
 * (CN)在连接接收端 ready 之后，通过此接收口获取接收端的授权类型，是否加了投屏密码之类的。
 * (EN)When receiver is READY, call this API to check whether receiver is protected by password.
 *
 * @param context
 * @return
 */
USBDISPLAY_CLIENT_SDK_API const UsbDisplayAuthenticateType &UsbDisplayClientGetReceiverAuthenticateType(UsbDisplayClientContext *context);

/**
 * (CN)在连接成功之后，通过 UsbDisplayClientGetReceiverAuthenticateType 如果接收端有密码保护，需要通过这个接口传达投屏密码到接收端进行授权。
 * (EN)When receiver is protected by password, use this API to authenticate the client device.
 *
 * @param context
 * @param pwd 密码值
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode UsbDisplayClientRequestAuthenticate(UsbDisplayClientContext *context, const char *pwd);

/**
 * (CN)通过 6 位数字投屏码来请求获取接收端信息。 6 位数据投屏码需要与一个云服务器配合。SDK会尝试连接云服务器，然后请求与投屏码对应的接收端信息，然后通过回调返回结果信息。
 * (EN)Try to get receiver info by 6 digits pin code. SDK would try to connect cloud server and query the receiver by pin code, and then callback the result.
 *
 * 回调：
 *  onPinCodeServerResponseReceiverAddress (CN)找到或没找到，都会通过这个回调通过。(EN)No matter found or not, would call this callback.
 * @param context
 * @param pinCode
 * @return
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode
UsbDisplayClientRequestGetReceiverAddressBy6DigitsPinCode(UsbDisplayClientContext* context, const char* address, const int port, const char *pinCode);

/**
 * (CN)通过 8 位数字投屏码来请求获取接收端信息。SDK 会尝试把这投屏码和本地所有网卡的IP进行转换组合，拼接成一个接收端的IP，然后尝试连接，如果连接成功，就会通过回调通知上层。
 * (EN)Try to get receiver info by 8 digits pin code. SDK would convert the pin code to an IP address, and try to connect, and would callback the result if found.
 *
 * 回调：
 *  onQueryReceiverBy8PinCodeFoundReceiver 找到接收端
 *  onQueryReceiverBy8PinCodeFoundNone 没有找到接收端
 *
 * @param context
 * @param pinCode
 * @return
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode
UsbDisplayClientRequestGetReceiverAddressBy8DigitsPinCode(UsbDisplayClientContext* context, const char *pinCode);

/**
 * 通过 5 位投屏码来请求接收端信息。这 5 位投屏码是数字和字母组合。拼接成 IP 的后三段地址。
 * @param context
 * @param pinCode
 * @return
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode
UsbDisplayClientRequestGetReceiverAddressBy5LocalPinCode(UsbDisplayClientContext *context, const char *pinCode);

/**
 * 通过 6 位投屏码来请求接收端信息。这 6 位投屏码是数字和字母组合，转换成 IP 地址。
 * @param context
 * @param pinCode
 * @return
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode
UsbDisplayClientRequestGetReceiverAddressBy6LocalPinCode(UsbDisplayClientContext *context, const char *pinCode);

/**
 * (CN)通过 IP 获取接收端信息。客户端与接收端在一个可连通的网络下，然后客户端会通过这个 IP 与接收端进行 UDP 通信，获取接收端的信息。
 * (EN)Try to get receiver info by IP address. The SDK would try to ping the receiver and get its info, and then callback the result.
 *
 *
 * @param context
 * @param ip 接收端的 IP 地址。如：192.168.1.213
 * @return
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode
UsbDisplayClientRequestGetReceiverInfo(UsbDisplayClientContext* context, const char *ip);


/**
 * (CN)当接收端启用了投屏密码，同时客户端使用了投屏码连接了接收端，这时需要客户端把投屏码提交给接收端验证，这样就可以不需要再输入投屏密码，简化用户的操作。
 * (EN)When the receiver is protected by password, and client connect the receiver using pin code. Pass the pin code to receiver and then no need the password.
 *
 * @param context
 * @param pinCode
 * @return
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode
UsbDisplayClientRequestAuthenticatePinCode(UsbDisplayClientContext* context, const char* pinCode);

/**
 * (CN)在反镜像过程中，对接收端进行触控操作。反镜像的画面显示在客户端一个窗口中，可能全屏也可能不全屏，所以需要客户端提供这个显示窗口的各个信息，然后 SDK 会对这些信息进行转换，得到接收端的位置信息。
 * (EN)When mirror back, client device can operate the receiver by sending touch data to receiver. The receiver's screen on client side would be full screen
 * or just a small window. So client should provide the window's information for SDK, and SDK would convert it to corresponding point to receiver.
 *
 *
 * @param context
 * @param offsetX 反镜像画面在客户端显示组件内的 X 偏移值。如果是占满整个 View 的，那么这个值就为 0.
 * @param offsetY 反镜像画面在客户端显示组件内的 Y 偏移值。如果是占满整个 View 的，那么这个值就为 0.
 * @param viewWidth 反镜像画面在客户端显示组件内的宽，如果占满了整个 View 组件，那么它的宽就等于 View 的宽。
 * @param viewHeight 反镜像画面在客户端显示组件内的高，如果占满了整个 View 组件，那么它的高就等于 View 的高。
 * @param pointCount 触控的点数
 * @param events 触控点的 XY 等信息。
 * @return
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode
UsbDisplayClientMirrorBackTouch(UsbDisplayClientContext* context,
                                int offsetX, int offsetY, int viewWidth, int viewHeight, int pointCount, const UsbDisplayTouchEvent events[]);

USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode
UsbDisplayClientRequestRefreshMirrorBackScreen(UsbDisplayClientContext * context);


USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode
UsbDisplayClientRequestLongPress(UsbDisplayClientContext * context);

USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode
UsbDisplayClientRequestCancelAuthorizeWaiting(UsbDisplayClientContext * context);


/**
 * 在已连接的情况下， 请求启动 BYOM 功能。需要告诉接收端，期望的视频格式、分辨率和帧率，音频的格式、采样率和声道数
 * @param context
 * @param videoFormat 1：YUV， 2：MPJG， 3：H264
 * @param videoResolution 1：480P， 2：720P， 3：1080P
 * @param videoFps 帧率数，一般10帧
 * @param audioFormat 音频格式。1：PCM， 2：AAC， 3：Mp3
 * @param audioSampleRate 采样率。1：44.1K， 2：48K
 * @param audioChannelCount 声道数。1：单声道，2：双声道
 * @return
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode
UsbDisplayClientRequestStartByom(UsbDisplayClientContext * context,
                                 int videoFormat,
                                 int videoResolution,
                                 int videoFps,
                                 int audioFormat,
                                 int audioSampleRate,
                                 int audioChannelCount);

/**
 * 请求关闭 BYOM 功能。
 * @param context
 * @return
 */
USBDISPLAY_CLIENT_SDK_API USBDisplayClientSDKErrorCode
UsbDisplayClientRequestStopByom(UsbDisplayClientContext * context);



#ifdef __cplusplus
}
#endif  /* __cplusplus */

#endif //USBDISPLAYCLIENTSDK_USBDISPLAYCLIENTSDK_H
